package personal.lijun.skeleton.springcloud.gateway.config;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import personal.lijun.skeleton.springcloud.gateway.handler.AuthenticationFaillHandler;
import personal.lijun.skeleton.springcloud.gateway.handler.AuthenticationSuccessHandler;
import personal.lijun.skeleton.springcloud.gateway.handler.CustomHttpBasicServerAuthenticationEntryPoint;

/**
 * @Auther: lijun@sstcsoft.com
 * @Date: 2019/9/20 11:43
 */
@Configuration
@EnableWebFluxSecurity()
public class SecurityConfig {
    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;
    @Autowired
    private AuthenticationFaillHandler authenticationFaillHandler;
    @Autowired
    private CustomHttpBasicServerAuthenticationEntryPoint customHttpBasicServerAuthenticationEntryPoint;

    //security的鉴权排除的url列表
    private static final String[] excludedAuthPages = {
            "/captcha-image",
            "/auth/login",
            "/auth/logout",
            "/health",
            "/api/socket/**",
            "/actuator/**",
//            "/resources/**",
            "/favicon.ico",
            "/api/**"
    };

    @Bean
    public SecurityWebFilterChain webFluxSecurityFilterChain(MyContextAwareServerHttpSecurity http) throws Exception {
        http
                .authorizeExchange()
                .pathMatchers(excludedAuthPages).permitAll()  //无需进行权限过滤的请求路径
                .pathMatchers(HttpMethod.OPTIONS).permitAll() //option 请求默认放行
                .pathMatchers("/resources/**").authenticated()
                .anyExchange().authenticated()
//                .and()
//                .httpBasic()
                .and()
                .requestCache().disable()
                .formLogin().loginPage("/auth/login")
                .authenticationSuccessHandler(authenticationSuccessHandler) //认证成功
                .authenticationFailureHandler(authenticationFaillHandler) //登陆验证失败
                .and().exceptionHandling().authenticationEntryPoint(customHttpBasicServerAuthenticationEntryPoint)  //基于http的接口请求鉴权失败
                .and().csrf().disable()//必须支持跨域
                .logout().disable()
                ;
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
//        return  NoOpPasswordEncoder.getInstance(); //默认不加密
        return new BCryptPasswordEncoder(4);
    }

    @Autowired(required = false)
    private ReactiveAuthenticationManager authenticationManager;

    @Autowired(required = false)
    private ReactiveUserDetailsService reactiveUserDetailsService;

    @Autowired(required = false)
    private PasswordEncoder passwordEncoder;

    @Autowired(required = false)
    private ReactiveUserDetailsPasswordService userDetailsPasswordService;

    @Autowired(required = false)
    private BeanFactory beanFactory;

    @Bean
    @Scope("prototype")
    public MyContextAwareServerHttpSecurity httpSecurity() {
        MyContextAwareServerHttpSecurity http = new MyContextAwareServerHttpSecurity();
        http
                .authenticationManager(authenticationManager())
                .headers().and()
                .logout().and();
        return http;
    }

    private ReactiveAuthenticationManager authenticationManager() {
        if (this.authenticationManager != null) {
            return this.authenticationManager;
        }
        if (this.reactiveUserDetailsService != null) {
            UserDetailsRepositoryReactiveAuthenticationManager manager =
                    new UserDetailsRepositoryReactiveAuthenticationManager(this.reactiveUserDetailsService);
            if (this.passwordEncoder != null) {
                manager.setPasswordEncoder(this.passwordEncoder);
            }
            manager.setUserDetailsPasswordService(this.userDetailsPasswordService);
            return manager;
        }
        return null;
    }
}
